package GUIMain;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import storage.DateTree;

import data.ClockInterface;
import data.ConfigInfo;
import data.FilePath;
import data.Reference;
import data.ReportInterface;
import data.ReportTools;
import errorAndDebug.MailingError;
import fileIO.XLSParser;

/**
 * This class allows a the program to automatically run in the background and 
 * send reports at intervals specified by the user
 * @author lax116
 *
 */
public class AutoRunGUI extends ClockInterface{
	private JDialog dialog;
	private  JButton close;
	private  JComboBox reportList;
	private  JTextField inboundDirText;
	private  JTextField inboundFileName;
	private  JTextField saveDirText;
	private  JCheckBox deleteOnCompleteCheck;
	private  JCheckBox stopOnErrorCheck;
	private  JButton browseInbound;
	private  JButton browseSave;
	private  JScrollPane timeJsp;
	private  DefaultListModel timeModel;
	private  JList timeList;
	private  JScrollPane dateJsp;
	private  DefaultListModel dateModel;
	private  JList dateList;
	private  boolean initialized = false;
	private  JButton start;
	private  JButton stop;
	private  JButton runNow;
	private  JTextField timeToNextRun;
	private  GridBagConstraints gbc;
	private DateTree dt;
	private boolean mutex = false;
	private ArrayList<String> errorList;
	private String sdfPattern;
	private boolean isRunning;
	
	public AutoRunGUI(){
		if(!initialized){
			initialized = true;
			initialize();
		}
		//Restore settings from last run if available
		inboundDirText.setText(ConfigInfo.AUTO_RUN_INBOUND_DIR);
		inboundFileName.setText(ConfigInfo.AUTO_RUN_INBOUND_FILE);
		saveDirText.setText(ConfigInfo.AUTO_RUN_SAVE_DIR);
		stopOnErrorCheck.setSelected(ConfigInfo.AUTO_RUN_STOP_ON_ERROR);
		deleteOnCompleteCheck.setSelected(ConfigInfo.AUTO_RUN_DELETE_ON_USE);
	}
	public void open(){
		GUIFocusManager.add(dialog);
	}
	private void initialize(){
		dialog = new JDialog();
		dialog.setTitle("Report Auto Run Setup");
		dialog.setLayout(new GridBagLayout());
		close = new JButton("Close");
		browseInbound = new JButton("Browse");
		browseSave = new JButton("Browse");
		start = new JButton("Start");
		stop = new JButton("Stop");
		inboundFileName = new JTextField();
		runNow = new JButton("Run Now");
		reportList = new JComboBox(ReportTools.getReportList().keySet().toArray());
		inboundDirText = new JTextField();
		saveDirText = new JTextField();
		deleteOnCompleteCheck = new JCheckBox("Delete Original File on Complete");
		stopOnErrorCheck = new JCheckBox("Terminate Scheduled Tasks on Error");
		timeModel = new DefaultListModel();
		timeList = new JList(timeModel);
		timeJsp = new JScrollPane(timeList);
		dateModel = new DefaultListModel();
		dateList = new JList(dateModel);
		dateJsp = new JScrollPane(dateList);
		timeToNextRun = new JTextField();
		timeToNextRun.setEditable(false);
		timeToNextRun.setPreferredSize(new Dimension(300,25));
		gbc = new GridBagConstraints();
		errorList = new ArrayList<String>();
		stop.setEnabled(false);
		isRunning = false;
		sdfPattern = ("MM/dd/yyyy : H:m:s |    ");
		
		gbc.insets.left = 2;
		gbc.insets.right = 2;
		gbc.insets.top = 2;
		gbc.insets.bottom = 2;
		
		gbc.gridheight = 1;
		gbc.gridwidth = 1;
		JPanel ioPanel = new JPanel();
		ioPanel.setLayout(new GridBagLayout());
		ioPanel.setBorder(BorderFactory.createTitledBorder("File I/O"));
		gbc.gridx = 0;
		gbc.gridy = 0;
		ioPanel.add(new JLabel("Report Type"),gbc);
		gbc.gridx = 1;
		gbc.gridwidth =1;
		reportList.setPreferredSize(new Dimension(300,25));
		ioPanel.add(reportList,gbc);
		gbc.gridwidth =1;
		gbc.gridy = 1;
		gbc.gridx = 0;
		ioPanel.add(new JLabel("Read Files From"),gbc);
		gbc.gridx = 1;
		inboundDirText.setPreferredSize(new Dimension(300,25));
		ioPanel.add(inboundDirText,gbc);
		gbc.gridx = 2;
		ioPanel.add(browseInbound,gbc);
		gbc.gridy = 2;
		gbc.gridx = 0;
		ioPanel.add(new JLabel("Read File Name"),gbc);
		gbc.gridx = 1;
		inboundFileName.setPreferredSize(new Dimension(300,25));
		ioPanel.add(inboundFileName,gbc);
		gbc.gridy = 3;
		gbc.gridx = 0;
		ioPanel.add(new JLabel("Save Files To"),gbc);
		gbc.gridx = 1;
		saveDirText.setPreferredSize(new Dimension(300,25));
		ioPanel.add(saveDirText,gbc);
		gbc.gridx = 2;
		ioPanel.add(browseSave,gbc);
		
		JPanel optionsPanel = new JPanel();
		optionsPanel.setLayout(new GridBagLayout());
		optionsPanel.setBorder(BorderFactory.createTitledBorder("Options"));
		optionsPanel.setPreferredSize(new Dimension(490,115));
		gbc.anchor = GridBagConstraints.LINE_START;
		gbc.gridx = 0;
		gbc.gridy = 0;
		optionsPanel.add(deleteOnCompleteCheck,gbc);
		gbc.gridy = 1;
		optionsPanel.add(stopOnErrorCheck,gbc);
		
		JPanel controlPanel = new JPanel();
		controlPanel.setLayout(new GridBagLayout());
		controlPanel.setBorder(BorderFactory.createTitledBorder("Controls"));
		controlPanel.setPreferredSize(new Dimension(490,115));
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.gridx = 0;
		gbc.gridy = 0;
		controlPanel.add(new JLabel("Time to next run:"),gbc);
		gbc.gridx = 1;
		gbc.gridy = 0;
		controlPanel.add(timeToNextRun,gbc);
		JPanel controlButtonPanel = new JPanel();
		controlButtonPanel.setLayout(new GridBagLayout());
		gbc.gridx = 0;
		gbc.gridy = 0;
		controlButtonPanel.add(start,gbc);
		gbc.gridx = 1;
		gbc.gridy = 0;
		controlButtonPanel.add(stop,gbc);
		gbc.gridx = 2;
		gbc.gridy = 0;
		controlButtonPanel.add(runNow,gbc);
		gbc.gridwidth = 2;
		gbc.gridx = 0;
		gbc.gridy = 1;
		controlPanel.add(controlButtonPanel,gbc);
		
		gbc.gridwidth = 1;
		gbc.anchor = GridBagConstraints.CENTER;
		JPanel rightPanel = new JPanel();
		rightPanel.setLayout(new GridBagLayout());
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.gridwidth = 2;
		rightPanel.add(new JLabel("Processing times:"),gbc);
		gbc.gridy = 1;
		gbc.gridx = 0;
		gbc.gridwidth = 1;
		rightPanel.add(new JLabel("Hours"),gbc);
		gbc.gridy = 1;
		gbc.gridx = 1;
		rightPanel.add(new JLabel("Days"),gbc);
		gbc.gridheight = 2;
		gbc.gridy = 2;
		gbc.gridx = 0;
		timeJsp.setPreferredSize(new Dimension(100,310));
		rightPanel.add(timeJsp,gbc);
		gbc.gridx = 1;
		dateJsp.setPreferredSize(new Dimension(100,310));
		rightPanel.add(dateJsp,gbc);
		
		//Close button
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new GridBagLayout());
		gbc.gridwidth  =1;
		gbc.gridheight = 1;
		gbc.gridx =0 ;
		gbc.gridy= 0;
		buttonPanel.add(close,gbc);
		
		gbc.anchor = GridBagConstraints.PAGE_START;
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.gridwidth = 2;
		gbc.gridheight =1;
		dialog.add(ioPanel,gbc);
		gbc.gridy = 1;
		dialog.add(optionsPanel,gbc);
		gbc.gridy = 2;
		dialog.add(controlPanel,gbc);
		gbc.gridwidth  =1;
		gbc.gridheight =3;
		gbc.gridy = 0;
		gbc.gridx = 2;
		dialog.add(rightPanel,gbc);
		gbc.gridx = 0;
		gbc.gridy= 3; 
		gbc.gridwidth = 3;
		gbc.gridheight = 1;
		dialog.add(buttonPanel,gbc);
		
		populateLists();
		addListeners();
		GUIFocusManager.setDialogProperties(dialog);
	}
	//Adds times to JLists
	private void populateLists(){
		
		for(int i=0;i<24;i++){
			timeModel.add(i, i+1 +":00");
		}
		dateModel.add(0, "Sunday");
		dateModel.add(1, "Monday");
		dateModel.add(2, "Tuesday");
		dateModel.add(3, "Wednesday");
		dateModel.add(4, "Thursday");
		dateModel.add(5, "Friday");
		dateModel.add(6, "Saturday");
	}
	private void addListeners(){
		close.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				GUIFocusManager.remove();
			}
		});
		browseInbound.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				FileChooser.openFileChooser(dialog, true);
				File f = FileChooser.getSelectedFile();
				if(f!=null){
					inboundDirText.setText(f.getAbsolutePath());
				}
			}
		});
		browseSave.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				FileChooser.openFileChooser(dialog, true);
				File f = FileChooser.getSelectedFile();
				if(f!=null){
					saveDirText.setText(f.getAbsolutePath());
				}
			}
		});
		start.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				//If all checks are passed, then begin running
				if(checkFields(true)){
					//If yes, lock all options 
					lock(false);
					//Start running
					GUILoader.getClock().addClockInterfaceListener(MainInterface.getAutoRunGUI());
					isRunning = true;
				}
			}
		});
		stop.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				MainInterface.getAutoRunGUI().stop();
			}
		});
		runNow.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				if(checkFields(false)){
					if(runReport(false)){
						JOptionPane.showMessageDialog(null,"Reports sent successfully");
					}
				}
			}
		});
	}
	/**
	 * Verifies all fields are filled before testing
	 * @param allChecks 
	 */
	protected boolean checkFields(boolean allChecks) {
		//Test if reporting can actually be started
		if(reportList.getSelectedIndex()<0){
			JOptionPane.showMessageDialog(null, "Please select a report type");
			reportList.requestFocus();
			return false;
		}
		File input = new File(inboundDirText.getText());
		if(!input.exists()||!input.isDirectory()){
			JOptionPane.showMessageDialog(null, "Invalid input directory specified!");
			inboundDirText.requestFocus();
			return false;
		}
		File output = new File(saveDirText.getText());
		if(!output.exists()||!output.isDirectory()){
			JOptionPane.showMessageDialog(null, "Invalid save directory specified!");
			saveDirText.requestFocus();
			return false;
		}
		if(allChecks && timeList.getSelectedIndices().length<1){
			JOptionPane.showMessageDialog(null, "At least one time period must be selected");
			timeList.requestFocus();
			return false;
		}
		if(allChecks && dateList.getSelectedIndices().length<1){
			JOptionPane.showMessageDialog(null, "At least one day of the week must be selected");
			dateList.requestFocus();
			return false;
		}
		if(!inboundFileName.getText().matches(Reference.XLS_REGEX)){
			JOptionPane.showMessageDialog(null, "Invalid read file name.  File name cannot contain symbols other than - . and must end in .xls");
			inboundFileName.requestFocus();
			return false;
		}

		//Clear errorLog
		errorList.clear();
		//Initialize tree
		dt = new DateTree(dateList.getSelectedIndices(),timeList.getSelectedIndices());
		//Set config options for future runs
		ConfigInfo.AUTO_RUN_INBOUND_DIR = inboundDirText.getText();
		ConfigInfo.AUTO_RUN_INBOUND_FILE = inboundFileName.getText();
		ConfigInfo.AUTO_RUN_SAVE_DIR = saveDirText.getText();
		ConfigInfo.AUTO_RUN_STOP_ON_ERROR = stopOnErrorCheck.isSelected();
		ConfigInfo.AUTO_RUN_DELETE_ON_USE = deleteOnCompleteCheck.isSelected();
		return true;
	}
	/**
	 * Prevents user from altering any options on this page 
	 * @param b true = lock ; false = unlock
	 */
	private void lock(boolean b){
		inboundDirText.setEnabled(b);
		saveDirText.setEnabled(b);
		timeList.setEnabled(b);
		dateList.setEnabled(b);
		start.setEnabled(b);
		stop.setEnabled(!b);
		deleteOnCompleteCheck.setEnabled(b);
		stopOnErrorCheck.setEnabled(b);
		reportList.setEnabled(b);
		inboundFileName.setEnabled(b);
		browseInbound.setEnabled(b);
		browseSave.setEnabled(b);
	}
	/**
	 * When called, this function will determine if an action should be taken
	 * and update the countdown timer
	 */
	@Override
	public void update(Calendar c) {
		Calendar closest = dt.getClosestDate(c);
		long diff = closest.getTimeInMillis() - c.getTimeInMillis();
		int days = (int)(diff/86400000);
		diff = diff%86400000;
		int hours = (int)(diff/3600000);
		diff = diff%3600000;
		int minutes = (int)(diff/60000);
		diff = diff%60000;
		int seconds = (int)(diff/1000);
		diff = diff%1000;
		//Unlock flag at every minute and a half
		if(minutes==0&& seconds ==30){
			mutex = false;
		}
		if(days+hours+minutes+seconds ==0&&!mutex){
			 runReport(true);
		}
		timeToNextRun.setText(days+ " days "+ hours + " hours " + minutes + " min " + seconds + " s");
	}
	public AutoRunGUI getInstance(){
		return this;
	}
	/**
	 * Runs the selected report
	 */
	public boolean runReport(boolean scheduled){
		mutex = true;
		 //Check validity of files
		 ReportInterface ri = ReportTools.getReport((String)reportList.getSelectedItem());
		 if(ri==null){
			 JOptionPane.showMessageDialog(null, "Report type " + reportList.getSelectedItem().toString() + "could not be located, terminating auto run.");
			 stop();
			 return false;
		 }
		 String filepath = inboundDirText.getText() + "\\" + inboundFileName.getText();
		 File inboundFile = new File(filepath);
		 if(!inboundFile.exists()||!inboundFile.isFile()){
			 errorList.add(GUILoader.getClock().getTime(sdfPattern)+ "File to be read could not be opened, Filepath: " + filepath);
			 if((!scheduled || stopOnErrorCheck.isSelected())){
				 stop();
				 return false;
			 }
		 }
		 File outputDir = new File(saveDirText.getText());
		 if(!outputDir.exists()){
			 outputDir.mkdirs();
		 }
		 HSSFWorkbook inboundWKB = null;
		try {
			inboundWKB = XLSParser.openXLS(filepath,true);
		} catch (Exception e) {
			 errorList.add(GUILoader.getClock().getTime(sdfPattern)+ e.getMessage());
			 if((!scheduled ||stopOnErrorCheck.isSelected())){
				 stop();
				 return false;
			 }
		}
		ArrayList<String> returnedErrors = ReportTools.getReport(reportList.getSelectedItem().toString()).run(inboundWKB, outputDir, false,true);
		//Some garbage collection
		inboundWKB = null;
		outputDir = null;
		
		for(int i=0;i<returnedErrors.size();i++){
			errorList.add(GUILoader.getClock().getTime(sdfPattern) + returnedErrors.get(i));
		}
		//Always check if not scheduled
		if((!scheduled || stopOnErrorCheck.isSelected()) &&!returnedErrors.isEmpty()){
			 stop();
			 return false;
		}
		//If delete original is checked, delete input file
		if(deleteOnCompleteCheck.isSelected()){
			inboundFile.delete();
		}
		inboundFile = null;
		return true;
	}
	/**
	 * Terminates auto run execution
	 */
	public void stop(){
		//Cease auto run of reports and enable controls
		GUILoader.getClock().removeClockInterfaceListener(MainInterface.getAutoRunGUI());
		timeToNextRun.setText("");
		//Check error log 
		if(!errorList.isEmpty()){
			//If not empty, write contents to file and save
			File filePath = new File(FilePath.ERROR_LOG_DIR+"\\"+GUILoader.getClock().getTime("MMddyyyy-Hms")+".txt");

			try {
				filePath.createNewFile();
				FileWriter writer = new FileWriter(filePath);
				for(int i=0;i<errorList.size();i++){
					writer.write(errorList.get(i) + "\n");
				}
				writer.close();
				int selection = JOptionPane.showConfirmDialog(null, "Errors encountered during runtime.  Log has been saved to " +
						filePath +". Do you want to view now?", "Errors encountered",JOptionPane.YES_NO_OPTION);
				if(selection == JOptionPane.YES_OPTION){
					//display error viewer
					MailingError.open(errorList);
				}
			} catch (IOException e) {
				int selection = JOptionPane.showConfirmDialog(null, "Errors encountered during runtime.  Log COULD NOT BE SAVED to " +
						filePath +". Do you want to view the contents now?", "Errors encountered",JOptionPane.YES_NO_OPTION);
				if(selection == JOptionPane.YES_OPTION){
					//display error viewer
					MailingError.open(errorList);
				}
			}
		}
		//Enable controls
		lock(true);	
		isRunning = false;
	}
	public boolean isRunning(){
		return isRunning;
	}
}
